home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Cool Demos, SDKs, & Tools / Demos⁄Tools⁄Offers / Alpha ƒ / Tcl / Packages / Docprojects.tcl < prev    next >
Text File  |  1999-04-26  |  34KB  |  1,035 lines

  1. ## -*-Tcl-*-
  2.  # ###################################################################
  3.  #  Vince's Additions - an extension package for Alpha
  4.  # 
  5.  #  FILE: "Docprojects.tcl"
  6.  #                                    created: 29/7/97 {4:59:22 pm} 
  7.  #                                last update: 04/26/1999 {16:32:59 PM} 
  8.  #  Author: Vince Darley
  9.  #  E-mail: <darley@fas.harvard.edu>
  10.  #    mail: Division of Engineering and Applied Sciences, Harvard University
  11.  #          Oxford Street, Cambridge MA 02138, USA
  12.  #     www: <http://www.fas.harvard.edu/~darley/>
  13.  #  
  14.  # Copyright (c) 1997-1999  Vince Darley, all rights reserved
  15.  # 
  16.  # See the file "license.terms" for information on usage and redistribution
  17.  # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  18.  # ###################################################################
  19.  ##
  20.  
  21. alpha::extension documentProjects 1.6.4 {
  22.     set alpha::prefs(documentProjects) Docproj
  23.     alpha::package require AlphaTcl 7.2fc7
  24.     namespace eval Docproj {}
  25.     # dummy value
  26.     ensureset docProject(name) [list "None" "Project2" "Thesis"]
  27.     # The name of the current project.  Every project has a unique name
  28.     newPref var currentProject "None" Docproj "" docProject(name) "varitem"
  29.     # Different identities can be useful if your projects may be sometimes
  30.     # for work purposes, sometimes for your own purposes etc.
  31.     newPref var identity Usual Docproj Docproj::changeIdentity identities "array"
  32.     menu::buildProc "Current Project" \
  33.       {menu::buildFlagMenu "Current Project" list currentProject DocprojmodeVars}
  34.     menu::insert packages submenu 1 {Current Project}
  35.     menu::insert packages items 1 \
  36.       "documentProjectPrefs…" "userDetails…" \
  37.       "<E<SremoveDocumentTemplate…" "<S<BeditDocumentTemplate…" \
  38.       "<SnewDocumentTemplate…" \
  39.       "<E<SremoveProject…" "<S<BeditProject…" "<SnewProject…"
  40.     # Key-binding to update the version number in a file's header.
  41.     # These version numbers can be inserted by some of the standard
  42.     # document templates.
  43.     newPref binding updateFileVersion "/f<U" Docproj
  44.     menu::insert winUtils items end \
  45.       "updateDate" \
  46.       "[menu::bind DocprojmodeVars(updateFileVersion) -]"
  47.     lunion elec::MenuTemplates "createHeader" "newDocument"
  48.     catch "unBind F1 bind::Completion"
  49.     menu::insert elec items end \
  50.       {Menu -n FunctionComments -p menu::generalProc {
  51.     "/eusual"    
  52.     "/e<Isimple" 
  53.     "/e<OwithAuthor" 
  54.     "/e<Uupdate" 
  55.     }}
  56.     hook::register requireOpenWindowsHook [list $electricMenu FunctionComments] 1
  57.     namespace eval newDocument {}
  58.     set "newDocument::handlers(Document Projects)" Docproj::newHandler
  59.     # Use this simple proc if we don't have the newDocument package.
  60.     if {![alpha::package exists newDocument]} {
  61.     ;proc file::newDocument {} {
  62.         beep
  63.         Docproj::newHandler [list -n [statusPrompt "New doc name:"]]
  64.     }
  65.     } else {
  66.     alpha::package require newDocument
  67.     }
  68.     
  69.     # When you request a new document, if this flag is set the user
  70.     # is only prompted with a list of document templates which 
  71.     # are relevant to the current mode.  This can be useful if you 
  72.     # have lots of templates.
  73.     newPref flag docTemplatesModeSpecific 1 Docproj
  74.     # When a file is saved, its header (time-stamp) etc can be
  75.     # automatically updated.
  76.     newPref flag autoUpdateHeader 1 Docproj
  77.     # call on saveHook
  78.     proc Docproj::changeProject {name} {
  79.     if {$name == "*"} { return }
  80.     menu::flagProc "Current Project" $name
  81.     }
  82.     
  83.     # call on saveHook
  84.     hook::register saveHook updateHeaderHook
  85. } maintainer {
  86.     "Vince Darley" darley@fas.harvard.edu <http://www.fas.harvard.edu/~darley/>
  87. } uninstall {this-file} help {file "Documentprojects Help"}
  88.  
  89. # user projects
  90. if {![info exists docProject(addendum)]} {
  91.     set docProject(addendum) { {none} {about some other stuff} {deep problems}}
  92.     set docProject(default_modes) { {} {C++ Tcl} {TeX}}
  93.     set docProject(extra) [list "" "Freely distributable" "Copyright (C) 1997-1998 the author."]
  94.     set docProject(license) [list "" "" ""]
  95. }
  96.  
  97.  
  98. proc updateHeaderHook {name} {
  99.     global DocprojmodeVars
  100.     if {$DocprojmodeVars(autoUpdateHeader)} {
  101.     # update does no harm if it fails so we call it for all
  102.     # modes with no worries.
  103.     getWinInfo -w $name a
  104.     if {$a(dirty)} {
  105.         file::updateDate $name
  106.     }
  107.     }
  108. }
  109.  
  110. # header/source templates (NOTE: FORMAT OF THIS LIST MAY CHANGE)
  111. llunion elec::DocTemplates 1 \
  112.   { * "Empty" * "" *} \
  113.   { * "Default" * t_default *} \
  114.   { TeX "Basic LaTeX document" "None" t_latex * {article report letter book slides}} \
  115.   { C++ "Basic C++ header file" "Header" t_cpp_header * } \
  116.   { C++ "Basic C++ source file" "Source" t_cpp_source * } \
  117.   { HTML "HTML document" * t_html * } 
  118. ## 
  119.  # \
  120.  # { C++ "Cpptcl Class Source" Source t_cpptcl_source "Cpptcl"} \
  121.  # { C++ "Cpptcl Class Header" Header t_cpptcl_header "Cpptcl"} \
  122.  # { Tcl "Itcl Class" * t_itcl_class "Cpptcl"}  \
  123.  # { Tcl "Blank Tcl Header" Header "\#" "Vince's Additions"} \
  124.  # { C++ "EvoX Class Source" Source t_cpptcl_source "EvoX"} \
  125.  # { C++ "EvoX Class Header" Header t_cpptcl_header "EvoX"}
  126.  ##
  127.  
  128. # used for file description headers
  129. if {$synchroniseWithInternetConfig} {
  130.     catch {set user(author) [icGetPref RealName]}
  131.     catch {set user(email) "<[icGetPref Email]>"}
  132.     catch {set user(www) "<[icGetPref WWWHomePage]>"}
  133.     catch {set user(organisation) [icGetPref Organization]}
  134. ensureset user(author) "Ken McKen"
  135. ensureset user(email) "ken@kenny.com"
  136. ensureset user(www) "http://www.kenny.com/"
  137. ensureset user(organisation) "Ken Corp."
  138.  
  139. ensureset user(address) "Rose St, MA 02143, USA"
  140. ensureset user(author_initials) "VMD"
  141.  
  142. ensureset identities(Usual) [array get user]
  143.  
  144. proc Docproj::changeIdentity {var} {
  145.     global identities user DocprojmodeVars
  146.     array set user $identities($DocprojmodeVars($var))
  147. }
  148.  
  149. if {[info exists DocprojmodeVars(identity)]} {
  150.     Docproj::changeIdentity identity
  151. }
  152.  
  153. proc global::userDetails {} {
  154.     global DocprojmodeVars modifiedArrayElements user identities
  155.     set oldInfo [array get user]
  156.     if {[catch {dialog::pkg_options "Docprojects" \
  157.       "User Details (some may be from Internet Config)" 1 user}] \
  158.       || ($oldInfo == [array get user])} {
  159.     return
  160.     }
  161.     set oldId $DocprojmodeVars(identity)
  162.     if {![dialog::yesno -y "Update" -n "New Identity" \
  163.       "Update $DocprojmodeVars(identity) identity, or make a new one?"]} {
  164.     # Ask for new name
  165.     set name [eval prompt [list "Enter tag for new identity" \
  166.       "<Tag>" "Old ids:"] [array names identities]]
  167.     set identities($name) [array get user]
  168.     set DocprojmodeVars(identity) $name
  169.     # Have to store Usual id too.
  170.     lappend modifiedArrayElements [list $name identities] \
  171.       [list identity DocprojmodeVars]
  172.     } else {
  173.     set identities($oldId) [array get user]
  174.     }
  175.     lappend modifiedArrayElements [list $oldId identities]
  176. }
  177.  
  178. proc global::documentProjectPrefs {} {
  179.     dialog::pkg_options "Docproj" "Preferences for your Document Projects"
  180. }
  181.  
  182. proc Docproj::newHandler {args} {
  183.     set doc [file::createDocument "new $args"]
  184.     if {[getModifiers] & 72} {
  185.     file::pickProject
  186.     }
  187.     file::createHeader $doc
  188.     return ""
  189. }
  190.  
  191. proc file::pickProject {} {
  192.     global DocprojmodeVars docProject
  193.     set item [listpick -p "Pick a project…" -L $DocprojmodeVars(currentProject) \
  194.       $docProject(name)]
  195.     if {$item != ""} {Docproj::changeProject $item }
  196.     return $item
  197. }
  198.  
  199. proc file::projectName {} { 
  200.     global DocprojmodeVars
  201.     return $DocprojmodeVars(currentProject)
  202. }
  203.  
  204. proc file::projectAddendum {} {
  205.     global docProject DocprojmodeVars
  206.     return [lindex $docProject(addendum) \
  207.       [lsearch -exact $docProject(name) $DocprojmodeVars(currentProject)]]
  208. }
  209.  
  210. proc file::projectExtra {} {
  211.     global docProject DocprojmodeVars
  212.     return [lindex $docProject(extra) \
  213.       [lsearch -exact $docProject(name) $DocprojmodeVars(currentProject)]]
  214. }
  215. proc file::projectLicense {} {
  216.     global docProject DocprojmodeVars
  217.     set ret [lindex $docProject(license) \
  218.       [lsearch -exact $docProject(name) $DocprojmodeVars(currentProject)]]
  219.     if {$ret == ""} {
  220.     return "none"
  221.     } else {
  222.     return $ret
  223.     }
  224. }
  225.  
  226. namespace eval functioncomments {}
  227.  
  228. ## 
  229.  # ----------------------------------------------------------------------
  230.  #     
  231.  #    "file::functionComment" --
  232.  #    
  233.  #  This procedure generates a nice little comment box like this one here.
  234.  #    
  235.  #   Results:
  236.  #  Well it doesn't return anything, but it allows you to enter each item
  237.  #  simply, moving from one to the next with Tab
  238.  #    
  239.  #   Side effects:
  240.  #  Not much
  241.  #    
  242.  # ----------------------------------------------------------------------
  243.  ##
  244. proc functioncomments::usual { {simple ""} {author 0} } {
  245.     global user
  246.     set fn [getSelect]
  247.     set fn [lindex $fn end]
  248.     beginningOfLine
  249.     set t "-------------------------------------------------------------------------\r"
  250.     append t "\r"
  251.     append t "\"$fn\" --\r"
  252.     append t "\r •description•\r"
  253.     if { $simple != "simple" } {
  254.     append t "\rResults:\r •results•\r\rSide effects:\r •side effects•\r"
  255.     }
  256.     if {$author} {
  257.     append t "\r--Version--Author------------------Changes-------------------------------"
  258.     append t "\r   1.0     $user(email) original\r"
  259.     }
  260.     append t "-------------------------------------------------------------------------"
  261.     set t [file::commentTextBlock $t]
  262.     elec::CenterInsertion $t
  263. }
  264.  
  265. proc functioncomments::simple {} { return [functioncomments::usual simple 0]}
  266. proc functioncomments::withAuthor {} { return [functioncomments::usual "" 1] }
  267.  
  268. proc file::commentTextBlock {text} {
  269.     set cc [commentCharacters "Paragraph"]
  270.     set c [lindex $cc 2]
  271.     regsub -all "\[\r\n\]" $text "\r${c}" text
  272.     return "[lindex $cc 0]\r[lindex $cc 2]${text}\r[lindex $cc 1]\r"
  273. }
  274.  
  275. ## 
  276.  # -------------------------------------------------------------------------
  277.  #     
  278.  #    "file::functionCommentUpdate" --
  279.  #    
  280.  #  Handles updating of a version line like the one below
  281.  #    
  282.  # --Version--Author------------------Changes-------------------------------  
  283.  #    1.0     <darley@fas.harvard.edu> original
  284.  #    1.1     <darley@fas.harvard.edu> quickly updated with shift-F1
  285.  # -------------------------------------------------------------------------
  286.  ##
  287. proc functioncomments::update {} {
  288.     global user
  289.     set begin [lindex [commentCharacters Paragraph] 2]
  290.     goto [file::findLocally "${begin}--Version--Author"]
  291.     goto [nextLineStart [nextLineStart [getPos] ]]
  292.     goto [file::findLocally "${begin}-------"]
  293.     elec::Insertion "${begin}   •Version•     $user(email) •Changes•\r"
  294. }
  295.  
  296. ## 
  297.  # -------------------------------------------------------------------------
  298.  #     
  299.  #    "file::findLocally" --
  300.  #    
  301.  #  Looks around for a particular sequence of characters (or a regexp) and
  302.  #  returns the start of the closest fit, either fowards or backwards, or
  303.  #  "" if no match was found. 
  304.  # -------------------------------------------------------------------------
  305.  ##
  306. proc file::findLocally { chars {regexp 0} { pos "" } } {
  307.     if { $pos == "" } { set pos [getPos] }
  308.     
  309.     set found1 [lindex [search -s -f 0 -n -r $regexp -- "$chars" $pos] 0]
  310.     set found2 [lindex [search -s -f 1 -n -r $regexp -- "$chars" $pos] 0]
  311.     
  312.     if { $found1 != "" && $found2 != "" } { 
  313.     if {[expr ([pos::math $pos + 0] - [pos::math $found1 + 0]) \
  314.       <= ([pos::math $found2 + 0] - [pos::math $pos + 0]) ]} {
  315.         return $found1
  316.     } else {
  317.         return $found2
  318.     }
  319.     }
  320.     
  321.     # return whatever we can, possibly ""
  322.     if { $found1 != "" } {
  323.     return $found1
  324.     } else {
  325.     if { $found2 == "" } { 
  326.         message "Couldn't find: $chars"
  327.     }
  328.     return $found2
  329.     }
  330. }
  331.  
  332.  
  333. ## 
  334.  # -------------------------------------------------------------------------
  335.  #     
  336.  #    "file::updateFileVersion"    --
  337.  #    
  338.  #  Update the version number and information in the header block of a
  339.  #  file.  Copes with both my old and new formats.
  340.  #    
  341.  # -------------------------------------------------------------------------
  342.  ##
  343. proc file::updateFileVersion {} {
  344.     global user
  345.     # in case the user wishes to return quickly
  346.     pushPosition
  347.     
  348.     goto [minPos]
  349.     set begin [lindex [commentCharacters Paragraph] 2]
  350.     set pos [file::findLocally "_/_/_" 0]
  351.     if { $pos == "" || [pos::compare $pos > [pos::math [minPos] + 1000]]} {
  352.     set srch [quote::WhitespaceReg [quote::Regfind "${begin} " ]]
  353.     append srch {[0-9]+/[0-9]+/[0-9]+}
  354.     set pos [file::findLocally $srch 1]
  355.     if { $pos == "" } {
  356.         message "Couldn't find original version template."
  357.         set srch [quote::Regfind "${begin} "]
  358.         append srch "See header file for further information"
  359.         set pos [file::findLocally [quote::WhitespaceReg $srch]]
  360.         if { $pos != "" } {
  361.         set pos [nextLineStart $pos]
  362.         } else {
  363.         goto [minPos]
  364.         set pos [file::findLocally "${begin}\#\#\#"]
  365.         if { $pos == "" } { message "Couldn't find any header" ; return }
  366.         set pos [lindex [search -s -f 1 -n -- "${begin}\#\#\#" [nextLineStart $pos]] 0]
  367.         if { $pos == "" } { message "Couldn't find any header" ; return }
  368.         }
  369.         goto $pos
  370.         set t  "${begin}\r"
  371.         append t  "${begin} modified by  rev reason\r"
  372.         append t  "${begin} ---------- --- --- -----------\r"
  373.         append t  "${begin} [file::paddedDate] $user(author_initials) 1.0 original\r"
  374.         insertText $t
  375.         select $pos [getPos]
  376.         return ""
  377.     } else {
  378.         # This is the normal case.
  379.         # Find the last version number
  380.         set p [minPos]
  381.         while {[pos::compare $p != $pos]} {
  382.         set pos $p
  383.         set p [file::findLocally $srch 1 [nextLineStart $p] ]
  384.         }
  385.         set pos [nextLineStart $pos]
  386.     }    
  387.     } else {
  388.     # old style header
  389.     set pos [lineStart $pos]
  390.     replaceText $pos [nextLineStart $pos] ""
  391.     }
  392.     # Now pos is at the start of the line where we wish to insert
  393.     goto $pos
  394.     elec::Insertion "${begin} [file::paddedDate] $user(author_initials) •• ••\r"
  395.     message "Pop position to return to where you were."
  396.     return ""
  397. }
  398.  
  399. proc file::paddedDate {{when ""}} {
  400.     if {$when == ""} { set when [now] }
  401.     return [string range "[lindex [mtime $when short] 0]     " 0 9]
  402. }
  403.  
  404. proc file::created {{convert 1}} {
  405.     if {[catch {getFileInfo [win::Current] info}]} {
  406.     if {$convert} {
  407.         return [mtime [now]]
  408.     } else {
  409.         return [now]
  410.     }
  411.     } else {
  412.     if {$convert} {
  413.         return [mtime $info(created)]
  414.     } else {
  415.         return $info(created)
  416.     }
  417.     }        
  418. }
  419.  
  420.  
  421. ## 
  422.  # -------------------------------------------------------------------------
  423.  #     
  424.  #    "file::createHeader" --
  425.  #    
  426.  #  Insert a descriptive header into the current file.  Needs to be
  427.  #  tailored more to different modes, but isn't too bad right now.
  428.  #     
  429.  #  'forcemode' will force the file into that mode via emacs-like mode
  430.  #  entries on the top line of the file.
  431.  #     
  432.  #  'parent' gives the name of a class from which the generated file
  433.  #  descends (appropriate for C++, [incr Tcl] for example).
  434.  # 
  435.  # -------------------------------------------------------------------------
  436.  ##
  437. proc file::createHeader { {template ""} {parent "" } } {
  438.     # Make sure the current project is compatible with this mode
  439.     file::coordinateProjectForMode
  440.     if {$parent == ""} {set parent "•parent•"}
  441.     if {$template == ""} { set template [list "" "" "Header" "\#" "" ""] }
  442.     # make the header
  443.     if {[lindex $template 1] != "Empty" } {
  444.     set t ""
  445.     set class [file::className]
  446.     if {$class == "Untitled"} {set class "•class name•"}
  447.     set file [win::CurrentTail]
  448.     set docHeadType [lindex $template 2]
  449.     if {$docHeadType != "None" } {
  450.         append t [file::topHeader]
  451.         if {$docHeadType != "Basic"} {
  452.         if {$docHeadType == "Source" || [file::isSource $file]} {
  453.             # it's a source file
  454.             append t " See header file for further information\r"
  455.         } elseif {$docHeadType == "Header" || $docHeadType == "*" && [file::isHeader $file]} {
  456.             global user
  457.             append t " Description: \r"
  458.             append t "\r"
  459.             append t " History\r"
  460.             append t "\r"
  461.             append t " modified by  rev reason\r"
  462.             append t " ---------- --- --- -----------\r"
  463.             append t " [file::paddedDate [file::created 0]] $user(author_initials) 1.0 original\r"
  464.         } else {
  465.             # not header or source or basic... oh well!
  466.         }
  467.         }
  468.         append t "###################################################################"
  469.         set t [file::commentTextBlock $t]
  470.         global mode
  471.         global ${mode}::firstHeaderLine
  472.         if {[info exists ${mode}::firstHeaderLine]} {
  473.         regsub "\r" $t "[quote::Regsub [set ${mode}::firstHeaderLine]]\r" t                
  474.         } else {
  475.         regsub "\r" $t "-*-${mode}-*-\r" t
  476.         }
  477.     }
  478.     set procName [lindex $template 3]
  479.     if {$procName != "\#" && [info commands $procName] == ""} { 
  480.         global PREFS
  481.         if {[catch {uplevel \#0 source [list [file join $PREFS prefs.tcl]]}]} {
  482.         alertnote "An error occurred while loading \"prefs.tcl\"" 
  483.         global errorInfo
  484.         dumpTraces "prefs.tcl error" $errorInfo
  485.         error ""
  486.         }            
  487.     }
  488.     if {[catch {append t [eval $procName [list $class] [list $parent] [lindex $template 5]]}]} {
  489.         alertnote "An error occurred while calling \"$procName\"" 
  490.         global errorInfo
  491.         dumpTraces "$procName error" $errorInfo
  492.         error ""
  493.     }
  494.     goto [minPos]
  495.     elec::Insertion $t
  496.     }
  497.     return ""
  498. }
  499.  
  500. ## 
  501.  # -------------------------------------------------------------------------
  502.  #     
  503.  #    "file::createDocument" --
  504.  #    
  505.  #  Make a new document from a given template type.
  506.  #     
  507.  #  'forcemode' will force the file into that mode via emacs-like mode
  508.  #  entries on the top line of the file.
  509.  #     
  510.  # -------------------------------------------------------------------------
  511.  ##
  512. proc file::createDocument { {winCreate ""} {forcemode "" } } {
  513.     # pick a template
  514.     # if [fileIsHeader    $file]
  515.     global elec::DocTemplates mode DocprojmodeVars
  516.     # decide if its mode-specific or not
  517.     set f [lindex $winCreate 2]
  518.     if {$DocprojmodeVars(docTemplatesModeSpecific)} {
  519.     if {$forcemode != ""} {
  520.         set tlist [file::docTemplates $f $forcemode non]
  521.     } else {
  522.         set tlist [file::docTemplates $f $mode non]
  523.     }
  524.     } else {
  525.     set tlist [file::docTemplates $f "" non]
  526.     }
  527.     lappend tlist "<Create new document type>"
  528.     if {$non != ""} {
  529.     eval lappend tlist "----------------------------------------------------" [lsort $non]
  530.     }
  531.     set tchoice [listpick -p "Pick a document template to insert" -L "Default" $tlist]
  532.     if {$tchoice == "<Create new document type>"} {
  533.     set tchoice [file::newDocumentTemplate 1]
  534.     }
  535.     if {$tchoice == "----------------------------------------------------"} { error "" }
  536.     
  537.     set tinfo [file::docTemplateInfo $tchoice]
  538.     set subTypes [lindex $tinfo 5]
  539.     if {$subTypes != ""} {
  540.     # replace the list of options with just the one selected
  541.     set tinfo [lreplace $tinfo 5 5 [listpick -p "Pick a document subtype of $tchoice" $subTypes]]
  542.     }
  543.     if {$forcemode == "" && [lindex $tinfo 0] != "*"} {
  544.     set forcemode [lindex $tinfo 0]
  545.     }
  546.     if {$winCreate != ""} {
  547.     eval $winCreate
  548.     }
  549.     
  550.     if { $forcemode != "" && $mode != $forcemode} { 
  551.     changeMode $forcemode
  552.     }
  553.     # we need to do this to stop modes switching later if this file isn't
  554.     # obviously a '$mode' file.
  555.     global win::Modes
  556.     set win::Modes($f) $mode
  557.     # set the project
  558.     Docproj::changeProject [lindex $tinfo 4]
  559.     # if the current project doesn't like this mode, then switch
  560.     file::coordinateProjectForMode
  561.     return $tinfo
  562. }
  563.  
  564. proc file::docTemplates { {f ""} {modeSpecific ""} {other ""}} {
  565.     global elec::DocTemplates
  566.     if {$other != ""} { upvar $other noList }
  567.     set tlist ""
  568.     set noList ""
  569.     if {$f != "" && $f != "Untitled"} {
  570.     set m [file::whichModeForWin $f]
  571.     foreach t ${elec::DocTemplates} {
  572.         if {[file::docTemplateMatchExt $t $f $m]} {
  573.         lappend tlist [lindex $t 1]
  574.         } else {
  575.         lappend noList [lindex $t 1]
  576.         }
  577.     }        
  578.     } else {
  579.     foreach t ${elec::DocTemplates} {
  580.         if {$modeSpecific == "" || [string match [lindex $t 0] $modeSpecific]} {
  581.         lappend tlist [lindex $t 1]
  582.         } else {
  583.         lappend noList [lindex $t 1]
  584.         }
  585.     }        
  586.     }    
  587.     return [lsort $tlist]
  588. }
  589.  
  590. proc file::docTemplateMatchExt {t f {m ""}} {
  591.     if {$m == ""} {set m [file::whichModeForWin $f]}
  592.     # match everything to a file with no particular extension
  593.     if {$m == "Text"} { return 1 }
  594.     set l [lindex $t 0]
  595.     set mMatch [expr [lsearch -exact $l $m] != -1]
  596.     switch -- [lindex $t 2] {
  597.     "None" -
  598.     "Basic" -
  599.     "*" {
  600.         if {$l == "*"} {
  601.         return 1
  602.         } else {
  603.         return $mMatch
  604.         }
  605.     }
  606.     "Header" {
  607.         if {$mMatch} {
  608.         return [file::isHeader $f $m]
  609.         }
  610.     }
  611.     "Source" {
  612.         if {$mMatch} {
  613.         return [file::isSource $f $m]
  614.         }
  615.         
  616.     }
  617.     }
  618.     return 0
  619. }
  620.  
  621. proc file::docTemplateInfo {name} {
  622.     global elec::DocTemplates
  623.     foreach t ${elec::DocTemplates} {
  624.     if {$name == [lindex $t 1]} {
  625.         return $t
  626.     }
  627.     }
  628. }
  629. proc file::docTemplateIndex {name} {
  630.     set i 0
  631.     global elec::DocTemplates
  632.     foreach t ${elec::DocTemplates} {
  633.     if {$name == [lindex $t 1]} {
  634.         return $i
  635.     }
  636.     incr i
  637.     }
  638. }
  639.  
  640. proc file::notTextMode {} {
  641.     global mode mode::features
  642.     if { $mode == "Text" } {
  643.     # we probably don't want Text mode     
  644.     set m [listpick -p "Pick a mode:" -L "Text" [array names mode::features]]
  645.     if { $m == "" } {set m "Text"}
  646.     changeMode $m
  647.     } 
  648. }
  649.  
  650. ## 
  651.  # -------------------------------------------------------------------------
  652.  #     
  653.  #    "file::topHeader"    --
  654.  #    
  655.  #  Inserts the top part of a descriptive header into the current file
  656.  # -------------------------------------------------------------------------
  657.  ##
  658. proc file::topHeader { } {
  659.     global user
  660.     set file [win::CurrentTail]
  661.     if {[catch {getFileInfo [win::Current] info}]} {
  662.     set created [mtime [now]]
  663.     set last_update $created
  664.     } else {
  665.     set created [mtime $info(created)]
  666.     set last_update [mtime $info(modified)]
  667.     }        
  668.     append t "###################################################################\r"
  669.     if {[file::projectName] != "*"} {
  670.     append t " [file::projectName] - [file::projectAddendum]\r"
  671.     }
  672.     append t "\r" 
  673.     append t " FILE: \"" $file "\"\r"
  674.     append t "                                   created: $created \r"
  675.     append t "                               last update: $last_update \r"    
  676.     append t " Author: $user(author)\r"
  677.     append t " E-mail: $user(email)\r"
  678.     if {$user(organisation) != ""} {
  679.     append t "   mail: $user(organisation)\r"
  680.     }
  681.     if {$user(address) != ""} {
  682.     append t "         $user(address)\r"
  683.     }
  684.     if {$user(www) != ""} {
  685.     append t "    www: $user(www)\r"
  686.     }
  687.     append t " \r"
  688.     append t [file::[file::projectLicense]]
  689.     if {[set e [file::projectExtra]] != ""} {
  690.     append t "[breakIntoLines $e]\r \r"
  691.     }
  692.     return $t
  693. }
  694.  
  695. ## 
  696.  # -------------------------------------------------------------------------
  697.  #     
  698.  #    "file::className"    --
  699.  #    
  700.  #  Extract root of file name as a class name for the file (obviously most
  701.  #  relevant to C++)
  702.  # -------------------------------------------------------------------------
  703.  ##
  704. proc file::className {} { return [file::baseName [win::CurrentTail]] }
  705.  
  706.  
  707. ## 
  708.  # -------------------------------------------------------------------------
  709.  #   
  710.  #  "file::coordinateProjectForMode" --
  711.  #  
  712.  #   When we create a new file or header automatically, it contains
  713.  #   information about our current project (as defined in docProject(...)).
  714.  #   Unfortunately we often forget to select the correct project first.
  715.  #   This procedure makes sure that your project is compatible with the
  716.  #   current mode, given the information in the 'docProject' array. If it isn't
  717.  #   then the current project is changed if a better match can be found. 
  718.  #         
  719.  #  Results:
  720.  #   None
  721.  #  
  722.  #  Side effects:
  723.  #   The current project may be changed
  724.  # -------------------------------------------------------------------------
  725.  ##
  726. proc file::coordinateProjectForMode {} {
  727.     global mode docProject
  728.     set currProj [file::projectName]
  729.     set projModes [lindex $docProject(default_modes) \
  730.       [lsearch -exact $docProject(name) [file::projectName]]]
  731.     if { $projModes != "" && [lsearch -exact $projModes $mode] == -1 } {
  732.     # this project doesn't like this mode.
  733.     # see if there's a better one
  734.     foreach modeLists $docProject(default_modes) {
  735.         if { [lsearch -exact $modeLists $mode] != -1 } {
  736.         # found a fit
  737.         set index [lsearch -exact $docProject(default_modes) $modeLists]
  738.         set proj [lindex $docProject(name) $index]
  739.         Docproj::changeProject "$proj"
  740.         return
  741.         }
  742.     }
  743.     }
  744. }
  745.  
  746. proc file::createNewClass {} {
  747.     global mode
  748.     # if the current project doesn't like this mode, then switch
  749.     file::coordinateProjectForMode
  750.     beep
  751.     set class [statusPrompt "A name for the new class:"]
  752.     set parent [statusPrompt "Descended from:" ]
  753.     switch -- $mode {
  754.     "C" -
  755.     "C++" {
  756.         file::createHeader [file::createDocument "new -n ${class}.cc" C++] $parent
  757.         file::createHeader [file::createDocument "new -n ${class}.h" C++] $parent
  758.     } 
  759.     "Tcl" {
  760.         file::createHeader [file::createDocument "new -n ${class}.tcl" Tcl] $parent
  761.     }
  762.     default {
  763.         message "No class procedure defined for your mode. Why not write one yourself?"
  764.     }
  765.     
  766.     }            
  767.     
  768. }
  769.  
  770.  
  771. ## 
  772.  # -------------------------------------------------------------------------
  773.  #   
  774.  # "file::updateGeneralDate" --
  775.  #  
  776.  #  Updates the date in the header of a file.  Normally this is the 
  777.  #  'last update' date, but we can override that if desired.
  778.  # -------------------------------------------------------------------------
  779.  ##
  780. proc file::updateGeneralDate { name {patt ""} {time ""}} {
  781.     if {$patt == ""} {set patt {last update: }}
  782.     regsub -all { } $patt "\[ \t\]" spatt
  783.     set pos [getPos]
  784.     set end [selEnd]
  785.     set hour {[0-9][0-9]?(:|\.)[0-9][0-9]((:|\.)[0-9][0-9])?([ \t][APap][Mm])?}
  786.     set date {[0-9][0-9]?(/|\.|\-)[0-9][0-9]?(/|\.|\-)[0-9][0-9]([0-9][0-9])?}
  787.     append spatt "\[ \t\]*" $date "(\[ \t]\{?" $hour {\}?)?}
  788.     set datePos [search -s -n -f 1 -r 1 -m 0 -l [pos::math [minPos] + 1000] $spatt [minPos]]
  789.     if {![llength $datePos]} {return}
  790.     if {$time == ""} {set time [mtime [now] short]}
  791.     if {[eval getText $datePos] == $time} {return}
  792.     eval replaceText $datePos [list $patt $time]
  793.     select $pos $end
  794. }
  795.  
  796. proc file::updateDate { {name ""} } {
  797.     set fr [win::Current]
  798.     if { $name == "" } {
  799.     set name $fr
  800.     }
  801.     if { $name != $fr } {
  802.     bringToFront $name
  803.     file::updateGeneralDate $name
  804.     bringToFront $fr
  805.     } else {
  806.     file::updateGeneralDate $name
  807.     }    
  808. }
  809.  
  810. proc file::updateCreationDate { name } {
  811.     if {[catch {getFileInfo [stripNameCount [win::Current]] info}]} {
  812.     set created [mtime [now]]
  813.     } else {
  814.     set created [mtime $info(created)]
  815.     }        
  816.     file::updateGeneralDate $name "created" $created
  817. }
  818.  
  819. proc file::newFunction {} {
  820.     elec::Insertion "[file::className]::•name•(•args•){\r\t•body•\r}\r"
  821. }
  822.  
  823. proc global::newDocumentTemplate { {subCall 0} } {
  824.     if {[catch {set newT [global::_editDocumentTemplate]}]} {return}
  825.     global elec::DocTemplates 
  826.     lappend elec::DocTemplates $newT
  827.     # save it permanently
  828.     global modifiedVars
  829.     lappend modifiedVars elec::DocTemplates
  830.     # add template to "prefs.tcl"
  831.     set procedure [lindex $newT 3]
  832.     set subproj [lindex $newT 5]
  833.     if {$procedure != "\#"} {
  834.     set def [file::_getDefault "Do you want to use this as the template?" t]
  835.     set t "\r"
  836.     append t "proc $procedure \{docname parentdoc"
  837.     if {$subproj != ""} { append t " subtype " }
  838.     append t "\} \{\r"
  839.     append t "\t# You must fill this in\r"
  840.     if {$subproj != ""} { append t "\t# Possible 'subtypes' are: $subproj\r" }
  841.     append t $def
  842.     append t "\r\treturn \$t\r\}\r"
  843.     addUserLine $t
  844.     if {[askyesno "I've added a template for the procedure to your 'prefs.tcl'. Do you want to edit it now?"] == "yes"} {
  845.         global::editPrefsFile
  846.         goto [maxPos]
  847.         if {$subCall} { 
  848.         alertnote "Once you've finished editing, hit cmd-N to go back and create a new document." 
  849.         # so our calling proc stops
  850.         error "Editing"
  851.         }
  852.     }
  853.     }
  854.     return [lindex $newT 1]
  855. }
  856.  
  857. proc file::_varValue {var} {
  858.     upvar $var a
  859.     if {[info exists a]} {
  860.     return $a
  861.     } else {
  862.     return ""
  863.     }
  864. }
  865.  
  866. proc file::_getDefault { text {default ""} {var ""}} {
  867.     if {[isSelection]} {
  868.     if {[askyesno "I notice you've selected some text. $text"] == "yes"} {
  869.         set default [getSelect]
  870.     } 
  871.     }
  872.     if {$default == ""} {
  873.     set default [getline "Enter template text (you can edit it later)" $default]
  874.     }
  875.     if {$var != ""} {
  876.     return [elec::_MakeIntoInsertion $default $var]
  877.     } else {
  878.     return $default
  879.     }
  880. }
  881.  
  882. proc global::_editDocumentTemplate {{def ""}} {
  883.     global DocprojmodeVars
  884.     if {$def == ""} {
  885.     set title "Create a new document template" 
  886.     set def {"" "" "By File Extension" "t_XXX" $DocprojmodeVars(currentProject) ""}
  887.     set new 1
  888.     } else {
  889.     set title "Edit document template" 
  890.     set new 0
  891.     }
  892.     
  893.     global docProject
  894.     set name ""
  895.     while { $name == ""} {
  896.     set y 40
  897.     set yb 220
  898.     set res [eval dialog -w 380 -h 340 \
  899.       [dialog::title $title 380] \
  900.       [dialog::button "OK" 290 yb] \
  901.       [dialog::button "Cancel" 290 yb] \
  902.       [dialog::textedit "Descriptive Name" [lindex $def 1] 10 y 15] \
  903.       [dialog::textedit "Modes (blank = all)" [lindex $def 0] 10 y 15] \
  904.       [dialog::textedit "Procedure name" [lindex $def 3] 10 y 15] \
  905.       [dialog::text "Descriptive header for this document template" 10 y] \
  906.       [dialog::text "(if 'Source', or 'Header', the mode must define" 10 y] \
  907.       [dialog::text "headerSuffices and sourceSuffices vars)" 10 y] \
  908.       [dialog::menu 10 y [list "None" "-" "Basic" "Source" "Header" "Either"] [lindex $def 2]] \
  909.       [dialog::text "Project name" 10 y] \
  910.       [dialog::menu 10 y $docProject(name) [lindex $def 4]] \
  911.       [dialog::textedit "List of sub-types" [lindex $def 5] 10 y 30] \
  912.       ]
  913.     if {[lindex $res 1]} { error "Cancel" } 
  914.     set i 1
  915.     foreach var {name modes procedure filetype proj subproj} {
  916.         set $var [lindex $res [incr i]]
  917.     }
  918.     if {$name == ""} { beep ; message "You must enter a name." }
  919.     }    
  920.     if {$modes == ""} {set modes "*"}
  921.     if {$filetype == "Either"} {set filetype "*"}
  922.     if {$proj == "None"} {set proj "*"}
  923.     if {$procedure == ""} {set procedure "\#"}
  924.     return [list $modes $name $filetype $procedure $proj $subproj]
  925.     
  926. }
  927.  
  928. proc global::editDocumentTemplate {} {
  929.     global modifiedVars elec::DocTemplates
  930.     set tlist [file::docTemplates] 
  931.     if {[catch {set l [listpick -p "Which document template do you want to edit?" $tlist]}]} {
  932.     return
  933.     }
  934.     set lind [file::docTemplateIndex $l]
  935.     if {[catch {set l [global::_editDocumentTemplate [file::docTemplateInfo $l]]}]} {
  936.     return
  937.     }
  938.     set elec::DocTemplates [lreplace ${elec::DocTemplates} $lind $lind $l]
  939.     lappend modifiedVars elec::DocTemplates
  940. }
  941.  
  942. proc global::removeDocumentTemplate {} {
  943.     global modifiedVars elec::DocTemplates
  944.     set tlist [file::docTemplates] 
  945.     if {[catch {set l [listpick -p "Which document template shall I permanently remove?" $tlist]}]} {
  946.     return
  947.     }
  948.     set l [file::docTemplateIndex $l]
  949.     set elec::DocTemplates [lreplace ${elec::DocTemplates} $l $l]
  950.     lappend modifiedVars elec::DocTemplates
  951. }
  952.  
  953. ## Create this sort of stuff.
  954.  # set docProject(name) [list    "None" "EvoX" "Vince's Additions" "Cpptcl"]
  955.  # set docProject(addendum) {    {none} {evolution in complex systems} \
  956.  #       {an extension package for Alpha}    {connecting    C++    with Tcl} }
  957.  # set docProject(default_modes) { {}    {C C++}    {Tcl} {C C++ Tcl}}
  958.  ##
  959. proc global::newProject {} {
  960.     global docProject
  961.     if {[catch {global::_editProject} res]} {return}
  962.     set i -1
  963.     foreach var {name addendum license extra default_modes} {
  964.     lappend docProject($var) [lindex $res [incr i]]
  965.     }
  966.     global modifiedArrVars
  967.     lappend modifiedArrVars docProject
  968.     addMenuItem -m {Current Project} [lindex $res 0]
  969.     Docproj::changeProject [lindex $res 0]
  970. }
  971. proc global::_editProject {{def ""}} {
  972.     if {$def == ""} {
  973.     set title "Create a new project"
  974.     set def [list "Vince's Additions" \
  975.       "an extension package for Alpha" "seeFileLicenseTerms" \
  976.       "See the file \"license.terms\" for information on usage and redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES." ""]
  977.     } else {
  978.     set title "Edit a project"
  979.     }
  980.     set y 40
  981.     set yb 270
  982.     global elec::LicenseTemplates
  983.     set res [eval dialog -w 380 -h 325 \
  984.       [dialog::title $title 360] \
  985.       [dialog::button "OK" 290 yb] \
  986.       [dialog::button "Cancel" 290 yb] \
  987.       [dialog::textedit "Short Descriptive Name" [lindex $def 0] 10 y 15] \
  988.       [dialog::textedit "Longer Description to append to the above" [lindex $def 1] 10 y 25] \
  989.       [dialog::text "License type for header comments" 10 y] \
  990.       [dialog::menu 10 y ${elec::LicenseTemplates} [lindex $def 2]] \
  991.       [dialog::textedit "Additional text for end of header comments" [lindex $def 3] 10 y 35 5] \
  992.       [dialog::textedit "Modes (blank = all)" [lindex $def 4] 10 y 15] \
  993.       ]
  994.     if {[lindex $res 1]} { error "Cancel" }
  995.     return [lrange $res 2 6]    
  996. }
  997.  
  998. proc global::editProject {} {
  999.     global docProject modifiedArrVars
  1000.     if {[catch {set l [listpick -p "Which project do you wish to edit?" \
  1001.       -L [file::projectName] $docProject(name)]}]} {
  1002.     return
  1003.     }
  1004.     
  1005.     set item [lsearch -exact $docProject(name) $l]
  1006.     foreach uvar {name addendum license extra default_modes} {
  1007.     lappend def [lindex $docProject($uvar) $item]
  1008.     }
  1009.     if {[catch {global::_editProject $def} def]} {return}
  1010.     set i -1
  1011.     foreach uvar {name addendum license extra default_modes} {
  1012.     set docProject($uvar) [lreplace $docProject($uvar) $item $item [lindex $def [incr i]]]
  1013.     }
  1014.     lappend modifiedArrVars docProject
  1015. }
  1016.  
  1017. proc global::removeProject {} {
  1018.     global docProject modifiedArrVars
  1019.     if {[catch {set l [listpick -p "Which project shall I permanently remove?" $docProject(name)]}]} {
  1020.     return
  1021.     }
  1022.     
  1023.     set item [lsearch -exact $docProject(name) $l]
  1024.     foreach uvar {name addendum license extra default_modes} {
  1025.     set docProject($uvar) [lreplace $docProject($uvar) $item $item]
  1026.     }
  1027.     lappend modifiedArrVars docProject
  1028.     if {[file::projectName] == $l} {
  1029.     Docproj::changeProject "None"
  1030.     }
  1031.     deleteMenuItem -m {Current Project} $l
  1032. }
  1033.  
  1034.